//#include "stdafx.h"

/*! \file String.h
 *	\author Дмитрий Литовченко kvakvs@yandex.ru
 *	Мягкая обёртка вокруг boost/algorithm/string.hpp с возможностью реализации собственных
 *	алгоритмов без подключения библиотеки Boost
 */
 
#include "ksstr.h"

#ifdef KSSTR_USING_UTF8
	#include <utfcpp/checked.h>
	#include <utfcpp/unchecked.h>
#endif 


#include <sstream>
#include <cctype>
//#include <mbstring.h>

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>

#pragma warning ( disable : 4996 )


// предварительно растягивать длину строк при конверсиях (теоретически ускоряет работу)
#define GB_PRERESERVE_STRINGS 1

namespace ksstr
{
 
  void toUpper( std::string & v )
{
	boost::algorithm::to_upper( v );
}

  std::string toUpperCopy( const std::string & v )
{
	return boost::algorithm::to_upper_copy( v );
}

  void toLower( std::string & v )
{
	boost::algorithm::to_lower( v );
}

  std::string toLowerCopy( const std::string & v )
{
	return boost::algorithm::to_lower_copy( v );
}

  void trimLeft( std::string & v )
{
	boost::algorithm::trim_left( v );
}

  std::string trimLeftCopy( const std::string & v )
{
	return boost::algorithm::trim_left_copy( v );
}

  void trimRight( std::string & v )
{
	boost::algorithm::trim_right( v );
}

  void trim( std::string & v )
{
	boost::algorithm::trim( v );
}

  std::string trimCopy( const std::string & v )
{
	return boost::algorithm::trim_copy( v );
}

  bool startsWith( const std::string & text, const std::string & search )
{
	return boost::algorithm::starts_with( text, search );
}


   //-----------------------

//!  \brief Начинается ли строка text с фрагмента search? (без учёта регистра)
  bool iStartsWith( const std::string & text, const std::string & search )
{
	return boost::algorithm::istarts_with( text, search );
}

//!  \brief Завершается ли строка text фрагментом search?
  bool endsWith( const std::string & text, const std::string & search )
{
	return boost::algorithm::ends_with( text, search );
}
//!  \brief Завершается ли строка text фрагментом search? (без учёта регистра)
  bool iEndsWith( const std::string & text, const std::string & search )
{
	return boost::algorithm::iends_with( text, search );
}

//!  \brief Содержит ли строка text фрагмент search?
  bool contains( const std::string & text, const std::string & search )
{
	return boost::algorithm::contains( text, search );
}
//!  \brief Содержит ли строка text фрагмент search? (без учёта регистра)
  bool iContains( const std::string & text, const std::string & search )
{
	return boost::algorithm::icontains( text, search );
}

 //---------------------


  bool iEquals( const std::string & a, const std::string & b )
{
	return boost::algorithm::iequals( a, b );
}





//------------------------------------------------

/**  \brief  Разбить строку value на слова по символам указанным в split_charset, 
результат  сложить в destination  */
  void split( std::vector <std::string> & destination, const std::string & value, const std::string & split_charset )
{
	boost::algorithm::split( destination, value, boost::algorithm::is_any_of( split_charset ) );
}

  void split( std::list <std::string> & destination, const std::string & value, const std::string & split_charset )
{
	boost::algorithm::split( destination, value, boost::algorithm::is_any_of( split_charset ) );
}

  void split( std::set <std::string> & destination, const std::string & value, const std::string & split_charset )
{
	boost::algorithm::split( destination, value, boost::algorithm::is_any_of( split_charset ) );
}

		
		
//!  \brief Заменить первое вхождение search в строку input новым текстом replace
  void replaceFirst( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::replace_first( input, search, replace );
}

//!  \brief Заменить первое вхождение search в строку input новым текстом replace, вернуть исправленную копию
  std::string replaceFirstCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::replace_first_copy( input, search, replace );
}

//!  \brief Заменить первое вхождение search в строку input новым текстом replace, без учёта регистра
  void iReplaceFirst( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::ireplace_first( input, search, replace );
}

//!  \brief Заменить первое вхождение search в строку input новым текстом replace, без учёта регистра, вернуть исправленную копию
  std::string iReplaceFirstCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::ireplace_first_copy( input, search, replace );
}
		
//!  \brief Удалить первое вхождение search в строку input
  void eraseFirst( std::string & input, const std::string & search )
{
	boost::algorithm::erase_first( input, search );
}

//!  \brief Удалить первое вхождение search в строку input, вернуть исправленную копию
  std::string eraseFirstCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::erase_first_copy( input, search );
}

//! Удалить первое вхождение search в строку input, без учёта регистра
  void iEraseFirst( std::string & input, const std::string & search )
{
	boost::algorithm::ierase_first( input, search );
}

/**  \brief Удалить первое вхождение search в строку input, без учёта регистра, 
  вернуть исправленную копию  */
  std::string iEraseFirstCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::ierase_first_copy( input, search );
}

//!  \brief Заменить последнее вхождение search в строку input новым текстом replace
  void replaceLast( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::replace_last( input, search, replace );
}

//!  \brief Заменить последнее вхождение search в строку input новым текстом replace, вернуть исправленную копию
  std::string replaceLastCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::replace_last_copy( input, search, replace );
}

//!  \brief Заменить последнее вхождение search в строку input новым текстом replace, без учёта регистра
  void iReplaceLast( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::ireplace_last( input, search, replace );
}

//!  \brief Заменить последнее вхождение search в строку input новым текстом replace, без учёта регистра, вернуть исправленную копию
  std::string iReplaceLastCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::ireplace_last_copy( input, search, replace );
}

//!  \brief Удалить последнее вхождение search в строку input
  void eraseLast( std::string & input, const std::string & search )
{
	boost::algorithm::erase_last( input, search );
}

//!  \brief Удалить последнее вхождение search в строку input, вернуть исправленную копию
  std::string eraseLastCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::erase_last_copy( input, search );
}

//!  \brief Удалить последнее вхождение search в строку input, без учёта регистра
  void iEraseLast( std::string & input, const std::string & search )
{
	boost::algorithm::ierase_last( input, search );
}

//!  \brief Удалить последнее вхождение search в строку input, без учёта регистра, вернуть исправленную копию
  std::string iEraseLastCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::ierase_last_copy( input, search );
}


/**  \brief Заменить N-е вхождение search в строку input новым текстом replace
   Отсчёт с нуля. Отрицательные N считаются с конца строки */
  void replaceNth( std::string & input, const std::string & search, int n, const std::string & replace )
{
	boost::algorithm::replace_nth( input, search, n, replace );
}

/**  \brief  Заменить N-e вхождение search в строку input новым текстом replace, 
      вернуть исправленную копию Отсчёт с нуля. 
      Отрицательные N считаются с конца строки  */
  std::string replaceNthCopy( const std::string & input, const std::string & search, int n, const std::string & replace )
{
	return boost::algorithm::replace_nth_copy( input, search, n, replace );
}

/**  \brief Заменить N-e вхождение search в строку input новым текстом replace, 
      без учёта регистра.  Отсчёт с нуля. Отрицательные N считаются с конца строки   */
  void iReplaceNth( std::string & input, const std::string & search, int n, const std::string & replace )
{
	boost::algorithm::ireplace_nth( input, search, n, replace );
}

/** \brief Заменить N-e вхождение search в строку input новым текстом replace, 
    без учёта регистра, вернуть исправленную копию  .
  Отсчёт с нуля. Отрицательные N считаются с конца строки */
  std::string iReplaceNthCopy( const std::string & input, const std::string & search, int n, const std::string & replace )
{
	return boost::algorithm::ireplace_nth_copy( input, search, n, replace );
}

/** \brief Удалить N-e вхождение search в строку input
    Отсчёт с нуля. Отрицательные N считаются с конца строки  */
  void eraseNth( std::string & input, const std::string & search, int n )
{
	boost::algorithm::erase_nth( input, search, n );
}

/** \brief Удалить N-e вхождение search в строку input, вернуть исправленную копию
    Отсчёт с нуля. Отрицательные N считаются с конца строки  */
  std::string eraseNthCopy( const  std::string & input, const std::string & search, int n )
{
	return boost::algorithm::erase_nth_copy( input, search, n );
}

/** \brief  Удалить N-e вхождение search в строку input, без учёта регистра
   Отсчёт с нуля. Отрицательные N считаются с конца строки  */
  void iEraseNth( std::string & input, const std::string & search, int n )
{
	boost::algorithm::ierase_nth( input, search, n );
}

/** \brief Удалить N-e вхождение search в строку input, без учёта регистра, вернуть исправленную копию
   Отсчёт с нуля. Отрицательные N считаются с конца строки  */
  std::string iEraseNthCopy( const std::string & input, const std::string & search, int n )
{
	return boost::algorithm::ierase_nth_copy( input, search, n );
}

//! \brief Заменить каждое вхождение search в строку input новым текстом replace
  void replaceAll( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::replace_all( input, search, replace );
}

//! \brief Заменить каждое вхождение search в строку input новым текстом replace, вернуть исправленную копию
  std::string replaceAllCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::replace_all_copy( input, search, replace );
}

//! \brief Заменить каждое вхождение search в строку input новым текстом replace, без учёта регистра
  void iReplaceAll( std::string & input, const std::string & search, const std::string & replace )
{
	boost::algorithm::ireplace_all( input, search, replace );
}

//! \brief Заменить каждое вхождение search в строку input новым текстом replace, без учёта регистра, вернуть исправленную копию
  std::string iReplaceAllCopy( const std::string & input, const std::string & search, const std::string & replace )
{
	return boost::algorithm::ireplace_all_copy( input, search, replace );
}

//! \brief Удалить каждое вхождение search в строку input
  void eraseAll( std::string & input, const std::string & search )
{
	boost::algorithm::erase_all( input, search );
}

//! \brief Удалить каждое вхождение search в строку input, вернуть исправленную копию
  std::string eraseAllCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::erase_all_copy( input, search );
}

//! \brief Удалить каждое вхождение search в строку input, без учёта регистра
  void iEraseAll( std::string & input, const std::string & search )
{
	boost::algorithm::ierase_all( input, search );
}

 
  std::string iEraseAllCopy( const std::string & input, const std::string & search )
{
	return boost::algorithm::ierase_all_copy( input, search );
}

 
  void replaceHead( std::string & input, int n, const std::string & replace )
{
	boost::algorithm::replace_head( input, n, replace );
}

 
  std::string replaceHeadCopy( const std::string & input, int n, const std::string & replace )
{
	return boost::algorithm::replace_head_copy( input, n, replace );
}

 
  void replaceTail( std::string & input, int n, const std::string & replace )
{
	boost::algorithm::replace_tail( input, n, replace );
}

 
  std::string replaceTailCopy( const std::string & input, int n, const std::string & replace )
{
	return boost::algorithm::replace_tail_copy( input, n, replace );
}
		
 
  void eraseHead( std::string & input, int n  )
{
	boost::algorithm::erase_head( input, n );
}

 
  std::string eraseHeadCopy( const std::string & input, int n )
{
	return boost::algorithm::erase_head_copy( input, n );
}

 
  void eraseTail( std::string & input, int n )
{
	boost::algorithm::erase_tail( input, n );
}

 
  std::string eraseTailCopy( const std::string & input, int n )
{
	return boost::algorithm::erase_tail_copy( input, n );
}

//-------------------------------------

void cstrToUpper(char* buf) 
{

	int c=0;
	while(true)
	{
		if(buf[c] == '\0')
		{
			break;
		}

		buf[c] = toupper(buf[c]);
		c++;
	}
}

 
void cstrToLower(char* buf) 
{
	int c=0;
	while(true)
	{
		if(buf[c] == '\0')
		{
			break;
		}

		buf[c] = tolower(buf[c]);
		c++;
	}
}


 //----------------------------------


  std::string trimRightCopy( const std::string & v )
{
	return boost::algorithm::trim_right_copy( v );
}

 



 //========================================================

#ifdef KSSTR_USING_UTF8
std::string toUtf8( const std::wstring & wide_str )
{
	std::string utf8_str;
#if GB_PRERESERVE_STRINGS
	// пессимистический прогноз все символы будут 2 байта
	utf8_str.reserve( wide_str.length() * 2 );
#endif
	utf8::utf16to8 (wide_str.begin(), wide_str.end(), std::back_inserter(utf8_str));
	return utf8_str;
}
#endif


#ifdef KSSTR_USING_UTF8
std::wstring toWide( const std::string & utf8_str )
{
	std::wstring wide_str;
#if GB_PRERESERVE_STRINGS
	// пессимистический прогноз все символы 1 байт
	wide_str.reserve( utf8_str.length() );
#endif
	utf8::utf8to16 (utf8_str.begin(), utf8_str.end(), std::back_inserter(wide_str));
	return wide_str;
}
#endif

unsigned int decodeUtf8Character( unsigned int & out_result, const char * utf8 )
{
	unsigned char c = utf8[0];
	out_result = L'?';

	if(c < 0x80)		// 1-byte code
	{
		out_result = c;
		return 1;
	}
	else if(c < 0xC0)     // invalid
	{
		return 1;
	}
	else if(c < 0xE0)	// 2-byte code
	{
		out_result =  (c & 0x1F) << 6;
		c = utf8[1];
		out_result |= (c & 0x3F);
		return 2;
	}
	else if(c < 0xF0)     // 3-byte code
	{
		out_result =  (c & 0x0F) << 12;
		c = utf8[1];
		out_result |= (c & 0x3F) <<  6;
		c = utf8[2];
		out_result |= (c & 0x3F);
		return 3;
	}
	else if(c < 0xF8)     // 4-byte code
	{
		// make sure wchar_t is large enough to hold it
		//if(std::numeric_limits<wchar_t>::max() > 0xFFFF)
		{
			out_result =  (c & 0x07) << 18;
			c = utf8[1];
			out_result |= (c & 0x3F) << 12;
			c = utf8[2];
			out_result |= (c & 0x3F) <<  6;
			c = utf8[3];
			out_result |= (c & 0x3F);
			return 4;
		}
	}
	return 1;
}

unsigned int uint32FromHex (const std::string & s)
{
	std::istringstream stream(s);
	unsigned int result;
	stream >> std::hex >> result;
	if (stream.fail() ) 
	{
		return 0;
	}

	return result;
}

unsigned long long uint64FromHex (const std::string & s)
{
	std::istringstream stream(s);
	unsigned long long result;
	stream >> std::hex >> result;
	if (stream.fail())
	{
		return 0;
	}

	return result;
}



#if  0 //! KSSTR_ALLOW_BOOST_LIBRARY
void toUpper( std::string & v )
{
// #ifdef _WIN32
// 	v = toUpperCopy(v);
// #else
	// Делаем морду валенком, как будто так и надо, и работаем только с ASCII
	for( size_t i = 0; i < v.size(); ++i ) v[i] = std::toupper( v[i] );
// #endif
}
#endif


#if  0 //! KSSTR_ALLOW_BOOST_LIBRARY
std::string toUpperCopy( const std::string & v )
{
// #ifdef _WIN32
// 	// TODO: вынести это куда-то глобально
// 	static _locale_t utf8Locale = _create_locale( LC_ALL, "en_US.UTF8" );
// 	// TODO: Не самый оптимальный вариант с копированием символов
// 	std::auto_ptr <unsigned char> tmp( new unsigned char [v.length()+1] );
// 	_mbscpy_s( tmp.get(), v.length()+1, (const unsigned char *)v.c_str() );
// 	_mbsupr_s_l( tmp.get(), v.length()+1, utf8Locale );
// 	return std::string( (const char *)tmp.get(), v.length() );
// #else
	std::string upper_v;
	upper_v.resize (v);
	// Делаем морду валенком, как будто так и надо, и работаем только с ASCII
	for( size_t i = 0; i < v.size(); ++i ) upper_v[i] = std::toupper( v[i] );
	return upper_v;
// #endif
}
#endif



#if  0 //! KSSTR_ALLOW_BOOST_LIBRARY
void toLower( std::string & v )
{
// #ifdef _WIN32
// 	v = toLowerCopy(v);
// #else
	std::string lower_v;
	lower_v.resize (v);
	// Делаем морду валенком, как будто так и надо, и работаем только с ASCII
	for( size_t i = 0; i < v.size(); ++i ) lower_v[i] = std::tolower( v[i] );
	return lower_v;
// #endif
}
#endif


#if  0 //! KSSTR_ALLOW_BOOST_LIBRARY
std::string toLowerCopy( const std::string & v )
{
// #ifdef _WIN32
// 	// TODO: вынести это куда-то глобально
// 	static _locale_t utf8Locale = _create_locale( LC_ALL, "en_US.UTF8" );
// 	// TODO: Не самый оптимальный вариант с копированием символов
// 	std::auto_ptr <unsigned char> tmp( new unsigned char [v.length()+1] );
// 	_mbscpy_s( tmp.get(), v.length()+1, (const unsigned char *)v.c_str() );
// 	_mbslwr_s_l( tmp.get(), v.length()+1, utf8Locale );
// 	return std::string( (const char *)tmp.get(), v.length() );
// #else
	std::string s( v );
	toLower( s );
	return s;
// #endif
}
#endif

//================================================================


void BinaryDataToSingleStrLine(std::string& dest, const void* data, const size_t datalen)
{
	dest = "";
 
	const char * srcdata = (const char*)data;
 
	char buff [32];
	for(size_t c=0; c<datalen; c++)
	{
		const unsigned char curr = (unsigned char)srcdata[c];

		unsigned int temp = (unsigned int )curr >> 4; 
		sprintf(buff, "%X", temp );

		temp = (unsigned int )curr;
		temp = temp << 28;
		temp = temp >> 28;
		sprintf(buff+1, "%X",  temp);


		//line += "0x";
		dest += buff;
 

	}
 

}

void BinaryDataToCppText(std::string& dest, const void* data, const size_t datalen)
{
	dest = "";
	dest += " static const   unsigned char rawData [] = \n{\n";

	const char * srcdata = (const char*)data;

	  size_t count_items = 0;
	  std::string line;
	  char buff [32];
	for(size_t c=0; c<datalen; c++)
	{
		const unsigned char curr = (unsigned char)srcdata[c];
 
			unsigned int temp = (unsigned int )curr >> 4; 
			sprintf(buff, "%X", temp );

			  temp = (unsigned int )curr;
			  temp = temp << 28;
			  temp = temp >> 28;
			sprintf(buff+1, "%X",  temp);
 

		line += "0x";
		line += buff;
		

		count_items++;
		if( count_items >= 8 )
		{
			count_items = 0;
			dest += line;
			line = "";
			dest += "\n";
		}
		else
		{
		   line += ", ";
		}


	}

	dest += "\n};";
 
}











 //================================================================


size_t cstrSearch_A(const char* src, const char* substr)
{
	if(src == NULL) return 0-1;
	if(substr == NULL) return 0-1;

	const  size_t SUBSTR_LEN = strlen(substr);
	if(SUBSTR_LEN == 0)
	{
		return 0-1;
	}

	size_t c=0;
	while(true)
	{
	    if(src[c] == '\0')
		{
			return 0-1;
		}

		size_t numCmpSucces =0;

		while(true)
		{
			if( src[c] == substr[numCmpSucces] )
			{
			   numCmpSucces ++;
			   c++;

			   if(numCmpSucces == SUBSTR_LEN)
			   {
				   // ok
				   return c - SUBSTR_LEN;
			   }

				continue;
			}
			else
			{
				numCmpSucces=0;
				break;
			}
		}

	c++;

	}
 
	return 0-1;
}


 
size_t cstrSearch_W(const wchar_t* src, const wchar_t* substr)
{
	if(src == NULL) return 0-1;
	if(substr == NULL) return 0-1;

	const  size_t SUBSTR_LEN = wcslen(substr);
	if(SUBSTR_LEN == 0)
	{
		return 0-1;
	}

	size_t c=0;
	while(true)
	{
		if(src[c] == L'\0')
		{
			return 0-1;
		}

		size_t numCmpSucces =0;

		while(true)
		{
			if( src[c] == substr[numCmpSucces] )
			{
				numCmpSucces ++;
				c++;

				if(numCmpSucces == SUBSTR_LEN)
				{
					// ok
					return c - SUBSTR_LEN;
				}

				continue;
			}
			else
			{
				numCmpSucces=0;
				break;
			}
		}

		c++;

	}

	return 0-1;
}


 
bool cstrSlicePosition_A(char* bufBefore, char* bufAfter, const char* src, const size_t position)
{
	if(!src) return  false;
	const size_t LEN = strlen(src);
	if(LEN==0) return false;

	if(position >= LEN) return false;

	*bufBefore = '\0';
	*bufAfter = '\0';

	size_t cbufBefore =0;
	size_t cbufAfter =0;

	size_t c=0;
	for( c=0; c<LEN; c++ )
	{
		if(c < position)
		{
			// before
			bufBefore[c] = src[c];
			bufBefore[c + 1] = '\0';
			cbufBefore = c;
			continue;
		}
		else
		{
			// after
			bufAfter[cbufAfter] = src[c];
			bufAfter[cbufAfter + 1] = '\0';
			cbufAfter++;

		}
 
	}

	return true;
}

 
bool cstrSlicePosition_W(wchar_t* bufBefore, wchar_t* bufAfter, const wchar_t* src, const size_t position)
{
	if(!src) return  false;
	const size_t LEN = wcslen(src);
	if(LEN==0) return false;

	if(position >= LEN) return false;

	*bufBefore = L'\0';
	*bufAfter = L'\0';

	size_t cbufBefore =0;
	size_t cbufAfter =0;

	size_t c=0;
	for( c=0; c<LEN; c++ )
	{
		if(c < position)
		{
			// before
			bufBefore[c] = src[c];
			bufBefore[c + 1] = L'\0';
			cbufBefore = c;
			continue;
		}
		else
		{
			// after
			bufAfter[cbufAfter] = src[c];
			bufAfter[c + 1] = L'\0';
			cbufAfter++;

		}
 
	}

	return true;
}


 



#if  0 //! KSSTR_ALLOW_BOOST_LIBRARY
bool startsWith( const std::string & text, const std::string & search )
{
	return text.substr( 0, search.length() ) == search;
}
#endif



 
} // namespace ksstr

#undef GB_PRERESERVE_STRINGS
